home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkTextIndex.c < prev    next >
C/C++ Source or Header  |  1995-06-28  |  21KB  |  829 lines

  1. /* 
  2.  * tkTextIndex.c --
  3.  *
  4.  *    This module provides procedures that manipulate indices for
  5.  *    text widgets.
  6.  *
  7.  * Copyright (c) 1992-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkTextIndex.c 1.11 95/06/28 16:16:36";
  15.  
  16. #include "default.h"
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19. #include "tkText.h"
  20.  
  21. /*
  22.  * Index to use to select last character in line (very large integer):
  23.  */
  24.  
  25. #define LAST_CHAR 1000000
  26.  
  27. /*
  28.  * Forward declarations for procedures defined later in this file:
  29.  */
  30.  
  31. static char *        ForwBack _ANSI_ARGS_((char *string,
  32.                 TkTextIndex *indexPtr));
  33. static char *        StartEnd _ANSI_ARGS_(( char *string,
  34.                 TkTextIndex *indexPtr));
  35.  
  36. /*
  37.  *--------------------------------------------------------------
  38.  *
  39.  * TkTextMakeIndex --
  40.  *
  41.  *    Given a line index and a character index, look things up
  42.  *    in the B-tree and fill in a TkTextIndex structure.
  43.  *
  44.  * Results:
  45.  *    The structure at *indexPtr is filled in with information
  46.  *    about the character at lineIndex and charIndex (or the
  47.  *    closest existing character, if the specified one doesn't
  48.  *    exist), and indexPtr is returned as result.
  49.  *
  50.  * Side effects:
  51.  *    None.
  52.  *
  53.  *--------------------------------------------------------------
  54.  */
  55.  
  56. TkTextIndex *
  57. TkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
  58.     TkTextBTree tree;        /* Tree that lineIndex and charIndex refer
  59.                  * to. */
  60.     int lineIndex;        /* Index of desired line (0 means first
  61.                  * line of text). */
  62.     int charIndex;        /* Index of desired character. */
  63.     TkTextIndex *indexPtr;    /* Structure to fill in. */
  64. {
  65.     register TkTextSegment *segPtr;
  66.     int index;
  67.  
  68.     indexPtr->tree = tree;
  69.     if (lineIndex < 0) {
  70.     lineIndex = 0;
  71.     charIndex = 0;
  72.     }
  73.     if (charIndex < 0) {
  74.     charIndex = 0;
  75.     }
  76.     indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
  77.     if (indexPtr->linePtr == NULL) {
  78.     indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
  79.     charIndex = 0;
  80.     }
  81.  
  82.     /*
  83.      * Verify that the index is within the range of the line.
  84.      * If not, just use the index of the last character in the line.
  85.      */
  86.  
  87.     for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
  88.         segPtr = segPtr->nextPtr) {
  89.     if (segPtr == NULL) {
  90.         indexPtr->charIndex = index-1;
  91.         break;
  92.     }
  93.     index += segPtr->size;
  94.     if (index > charIndex) {
  95.         indexPtr->charIndex = charIndex;
  96.         break;
  97.     }
  98.     }
  99.     return indexPtr;
  100. }
  101.  
  102. /*
  103.  *--------------------------------------------------------------
  104.  *
  105.  * TkTextIndexToSeg --
  106.  *
  107.  *    Given an index, this procedure returns the segment and
  108.  *    offset within segment for the index.
  109.  *
  110.  * Results:
  111.  *    The return value is a pointer to the segment referred to
  112.  *    by indexPtr;  this will always be a segment with non-zero
  113.  *    size.  The variable at *offsetPtr is set to hold the
  114.  *    integer offset within the segment of the character
  115.  *    given by indexPtr.
  116.  *
  117.  * Side effects:
  118.  *    None.
  119.  *
  120.  *--------------------------------------------------------------
  121.  */
  122.  
  123. TkTextSegment *
  124. TkTextIndexToSeg(indexPtr, offsetPtr)
  125.     TkTextIndex *indexPtr;        /* Text index. */
  126.     int *offsetPtr;            /* Where to store offset within
  127.                      * segment, or NULL if offset isn't
  128.                      * wanted. */
  129. {
  130.     register TkTextSegment *segPtr;
  131.     int offset;
  132.  
  133.     for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
  134.         offset >= segPtr->size;
  135.         offset -= segPtr->size, segPtr = segPtr->nextPtr) {
  136.     /* Empty loop body. */
  137.     }
  138.     if (offsetPtr != NULL) {
  139.     *offsetPtr = offset;
  140.     }
  141.     return segPtr;
  142. }
  143.  
  144. /*
  145.  *--------------------------------------------------------------
  146.  *
  147.  * TkTextSegToOffset --
  148.  *
  149.  *    Given a segment pointer and the line containing it, this
  150.  *    procedure returns the offset of the segment within its
  151.  *    line.
  152.  *
  153.  * Results:
  154.  *    The return value is the offset (within its line) of the
  155.  *    first character in segPtr.
  156.  *
  157.  * Side effects:
  158.  *    None.
  159.  *
  160.  *--------------------------------------------------------------
  161.  */
  162.  
  163. int
  164. TkTextSegToOffset(segPtr, linePtr)
  165.     TkTextSegment *segPtr;        /* Segment whose offset is desired. */
  166.     TkTextLine *linePtr;        /* Line containing segPtr. */
  167. {
  168.     TkTextSegment *segPtr2;
  169.     int offset;
  170.  
  171.     offset = 0;
  172.     for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
  173.         segPtr2 = segPtr2->nextPtr) {
  174.     offset += segPtr2->size;
  175.     }
  176.     return offset;
  177. }
  178.  
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * TkTextGetIndex --
  183.  *
  184.  *    Given a string, return the line and character indices that
  185.  *    it describes.
  186.  *
  187.  * Results:
  188.  *    The return value is a standard Tcl return result.  If
  189.  *    TCL_OK is returned, then everything went well and the index
  190.  *    at *indexPtr is filled in;  otherwise TCL_ERROR is returned
  191.  *    and an error message is left in interp->result.
  192.  *
  193.  * Side effects:
  194.  *    None.
  195.  *
  196.  *----------------------------------------------------------------------
  197.  */
  198.  
  199. int
  200. TkTextGetIndex(interp, textPtr, string, indexPtr)
  201.     Tcl_Interp *interp;        /* Use this for error reporting. */
  202.     TkText *textPtr;        /* Information about text widget. */
  203.     char *string;        /* Textual description of position. */
  204.     TkTextIndex *indexPtr;    /* Index structure to fill in. */
  205. {
  206.     register char *p;
  207.     char *end, *endOfBase;
  208.     Tcl_HashEntry *hPtr;
  209.     TkTextTag *tagPtr;
  210.     TkTextSearch search;
  211.     TkTextIndex first, last;
  212.     int wantLast, result;
  213.     char c;
  214.  
  215.     /*
  216.      *---------------------------------------------------------------------
  217.      * Stage 1: check to see if the index consists of nothing but a mar
  218.      * name.  We do this check now even though it's also done later, in
  219.      * order to allow mark names that include funny characters such as
  220.      * spaces or "+1c".
  221.      *---------------------------------------------------------------------
  222.      */
  223.  
  224.     if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
  225.     return TCL_OK;
  226.     }
  227.  
  228.     /*
  229.      *------------------------------------------------
  230.      * Stage 2: start again by parsing the base index.
  231.      *------------------------------------------------
  232.      */
  233.  
  234.     indexPtr->tree = textPtr->tree;
  235.  
  236.     /*
  237.      * First look for the form "tag.first" or "tag.last" where "tag"
  238.      * is the name of a valid tag.  Try to use up as much as possible
  239.      * of the string in this check (strrchr instead of strchr below).
  240.      * Doing the check now, and in this way, allows tag names to include
  241.      * funny characters like "@" or "+1c".
  242.      */
  243.  
  244.     p = strrchr(string, '.');
  245.     if (p != NULL) {
  246.     if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
  247.         wantLast = 0;
  248.         endOfBase = p+6;
  249.     } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
  250.         wantLast = 1;
  251.         endOfBase = p+5;
  252.     } else {
  253.         goto tryxy;
  254.     }
  255.     *p = 0;
  256.     hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
  257.     *p = '.';
  258.     if (hPtr == NULL) {
  259.         goto tryxy;
  260.     }
  261.     tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
  262.     TkTextMakeIndex(textPtr->tree, 0, 0, &first);
  263.     TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
  264.         &last);
  265.     TkBTreeStartSearch(&first, &last, tagPtr, &search);
  266.     if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
  267.         Tcl_AppendResult(interp,
  268.             "text doesn't contain any characters tagged with \"",
  269.             Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
  270.                 (char *) NULL);
  271.         return TCL_ERROR;
  272.     }
  273.     *indexPtr = search.curIndex;
  274.     if (wantLast) {
  275.         while (TkBTreeNextTag(&search)) {
  276.         *indexPtr = search.curIndex;
  277.         }
  278.     }
  279.     goto gotBase;
  280.     }
  281.  
  282.     tryxy:
  283.     if (string[0] == '@') {
  284.     /*
  285.      * Find character at a given x,y location in the window.
  286.      */
  287.  
  288.     int x, y;
  289.  
  290.     p = string+1;
  291.     x = strtol(p, &end, 0);
  292.     if ((end == p) || (*end != ',')) {
  293.         goto error;
  294.     }
  295.     p = end+1;
  296.     y = strtol(p, &end, 0);
  297.     if (end == p) {
  298.         goto error;
  299.     }
  300.     TkTextPixelIndex(textPtr, x, y, indexPtr);
  301.     endOfBase = end;
  302.     goto gotBase; 
  303.     }
  304.  
  305.     if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
  306.     int lineIndex, charIndex;
  307.  
  308.     /*
  309.      * Base is identified with line and character indices.
  310.      */
  311.  
  312.     lineIndex = strtol(string, &end, 0) - 1;
  313.     if ((end == string) || (*end != '.')) {
  314.         goto error;
  315.     }
  316.     p = end+1;
  317.     if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
  318.         charIndex = LAST_CHAR;
  319.         endOfBase = p+3;
  320.     } else {
  321.         charIndex = strtol(p, &end, 0);
  322.         if (end == p) {
  323.         goto error;
  324.         }
  325.         endOfBase = end;
  326.     }
  327.     TkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
  328.     goto gotBase;
  329.     }
  330.  
  331.     for (p = string; *p != 0; p++) {
  332.     if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
  333.         break;
  334.     }
  335.     }
  336.     endOfBase = p;
  337.     if (string[0] == '.') {
  338.     /*
  339.      * See if the base position is the name of an embedded window.
  340.      */
  341.  
  342.     c = *endOfBase;
  343.     *endOfBase = 0;
  344.     result = TkTextWindowIndex(textPtr, string, indexPtr);
  345.     *endOfBase = c;
  346.     if (result != 0) {
  347.         goto gotBase;
  348.     }
  349.     }
  350.     if ((string[0] == 'e')
  351.         && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
  352.     /*
  353.      * Base position is end of text.
  354.      */
  355.  
  356.     TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
  357.         0, indexPtr);
  358.     goto gotBase;
  359.     } else {
  360.     /*
  361.      * See if the base position is the name of a mark.
  362.      */
  363.  
  364.     c = *endOfBase;
  365.     *endOfBase = 0;
  366.     result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
  367.     *endOfBase = c;
  368.     if (result == TCL_OK) {
  369.         goto gotBase;
  370.     }
  371.     }
  372.     goto error;
  373.  
  374.     /*
  375.      *-------------------------------------------------------------------
  376.      * Stage 3: process zero or more modifiers.  Each modifier is either
  377.      * a keyword like "wordend" or "linestart", or it has the form
  378.      * "op count units" where op is + or -, count is a number, and units
  379.      * is "chars" or "lines".
  380.      *-------------------------------------------------------------------
  381.      */
  382.  
  383.     gotBase:
  384.     p = endOfBase;
  385.     while (1) {
  386.     while (isspace(UCHAR(*p))) {
  387.         p++;
  388.     }
  389.     if (*p == 0) {
  390.         break;
  391.     }
  392.     
  393.     if ((*p == '+') || (*p == '-')) {
  394.         p = ForwBack(p, indexPtr);
  395.     } else {
  396.         p = StartEnd(p, indexPtr);
  397.     }
  398.     if (p == NULL) {
  399.         goto error;
  400.     }
  401.     }
  402.     return TCL_OK;
  403.  
  404.     error:
  405.     Tcl_AppendResult(interp, "bad text index \"", string, "\"",
  406.         (char *) NULL);
  407.     return TCL_ERROR;
  408. }
  409.  
  410. /*
  411.  *----------------------------------------------------------------------
  412.  *
  413.  * TkTextPrintIndex --
  414.  *
  415.  *    
  416.  *    This procedure generates a string description of an index,
  417.  *    suitable for reading in again later.
  418.  *
  419.  * Results:
  420.  *    The characters pointed to by string are modified.
  421.  *
  422.  * Side effects:
  423.  *    None.
  424.  *
  425.  *----------------------------------------------------------------------
  426.  */
  427.  
  428. void
  429. TkTextPrintIndex(indexPtr, string)
  430.     TkTextIndex *indexPtr;    /* Pointer to index. */
  431.     char *string;        /* Place to store the position.  Must have
  432.                  * at least TK_POS_CHARS characters. */
  433. {
  434.     sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
  435.         indexPtr->charIndex);
  436. }
  437.  
  438. /*
  439.  *--------------------------------------------------------------
  440.  *
  441.  * TkTextIndexCmp --
  442.  *
  443.  *    Compare two indices to see which one is earlier in
  444.  *    the text.
  445.  *
  446.  * Results:
  447.  *    The return value is 0 if index1Ptr and index2Ptr refer
  448.  *    to the same position in the file, -1 if index1Ptr refers
  449.  *    to an earlier position than index2Ptr, and 1 otherwise.
  450.  *
  451.  * Side effects:
  452.  *    None.
  453.  *
  454.  *--------------------------------------------------------------
  455.  */
  456.  
  457. int
  458. TkTextIndexCmp(index1Ptr, index2Ptr)
  459.     TkTextIndex *index1Ptr;        /* First index. */
  460.     TkTextIndex *index2Ptr;        /* Second index. */
  461. {
  462.     int line1, line2;
  463.  
  464.     if (index1Ptr->linePtr == index2Ptr->linePtr) {
  465.     if (index1Ptr->charIndex < index2Ptr->charIndex) {
  466.         return -1;
  467.     } else if (index1Ptr->charIndex > index2Ptr->charIndex) {
  468.         return 1;
  469.     } else {
  470.         return 0;
  471.     }
  472.     }
  473.     line1 = TkBTreeLineIndex(index1Ptr->linePtr);
  474.     line2 = TkBTreeLineIndex(index2Ptr->linePtr);
  475.     if (line1 < line2) {
  476.     return -1;
  477.     }
  478.     if (line1 > line2) {
  479.     return 1;
  480.     }
  481.     return 0;
  482. }
  483.  
  484. /*
  485.  *----------------------------------------------------------------------
  486.  *
  487.  * ForwBack --
  488.  *
  489.  *    This procedure handles +/- modifiers for indices to adjust
  490.  *    the index forwards or backwards.
  491.  *
  492.  * Results:
  493.  *    If the modifier in string is successfully parsed then the
  494.  *    return value is the address of the first character after the
  495.  *    modifier, and *indexPtr is updated to reflect the modifier.
  496.  *    If there is a syntax error in the modifier then NULL is returned.
  497.  *
  498.  * Side effects:
  499.  *    None.
  500.  *
  501.  *----------------------------------------------------------------------
  502.  */
  503.  
  504. static char *
  505. ForwBack(string, indexPtr)
  506.     char *string;        /* String to parse for additional info
  507.                  * about modifier (count and units). 
  508.                  * Points to "+" or "-" that starts
  509.                  * modifier. */
  510.     TkTextIndex *indexPtr;    /* Index to update as specified in string. */
  511. {
  512.     register char *p;
  513.     char *end, *units;
  514.     int count, lineIndex;
  515.     size_t length;
  516.  
  517.     /*
  518.      * Get the count (how many units forward or backward).
  519.      */
  520.  
  521.     p = string+1;
  522.     while (isspace(UCHAR(*p))) {
  523.     p++;
  524.     }
  525.     count = strtoul(p, &end, 0);
  526.     if (end == p) {
  527.     return NULL;
  528.     }
  529.     p = end;
  530.     while (isspace(UCHAR(*p))) {
  531.     p++;
  532.     }
  533.  
  534.     /*
  535.      * Find the end of this modifier (next space or + or - character),
  536.      * then parse the unit specifier and update the position
  537.      * accordingly.
  538.      */
  539.  
  540.     units = p; 
  541.     while ((*p != 0) && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
  542.     p++;
  543.     }
  544.     length = p - units;
  545.     if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
  546.     if (*string == '+') {
  547.         TkTextIndexForwChars(indexPtr, count, indexPtr);
  548.     } else {
  549.         TkTextIndexBackChars(indexPtr, count, indexPtr);
  550.     }
  551.     } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
  552.     lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
  553.     if (*string == '+') {
  554.         lineIndex += count;
  555.     } else {
  556.         lineIndex -= count;
  557.  
  558.         /*
  559.          * The check below retains the character position, even
  560.          * if the line runs off the start of the file.  Without
  561.          * it, the character position will get reset to 0 by
  562.          * TkTextMakeIndex.
  563.          */
  564.  
  565.         if (lineIndex < 0) {
  566.         lineIndex = 0;
  567.         }
  568.     }
  569.     TkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
  570.         indexPtr);
  571.     } else {
  572.     return NULL;
  573.     }
  574.     return p;
  575. }
  576.  
  577. /*
  578.  *----------------------------------------------------------------------
  579.  *
  580.  * TkTextIndexForwChars --
  581.  *
  582.  *    Given an index for a text widget, this procedure creates a
  583.  *    new index that points "count" characters ahead of the source
  584.  *    index.
  585.  *
  586.  * Results:
  587.  *    *dstPtr is modified to refer to the character "count" characters
  588.  *    after srcPtr, or to the last character in the file if there aren't
  589.  *    "count" characters left in the file.
  590.  *
  591.  * Side effects:
  592.  *    None.
  593.  *
  594.  *----------------------------------------------------------------------
  595.  */
  596.  
  597.     /* ARGSUSED */
  598. void
  599. TkTextIndexForwChars(srcPtr, count, dstPtr)
  600.     TkTextIndex *srcPtr;        /* Source index. */
  601.     int count;                /* How many characters forward to
  602.                      * move.  May be negative. */
  603.     TkTextIndex *dstPtr;        /* Destination index: gets modified. */
  604. {
  605.     TkTextLine *linePtr;
  606.     TkTextSegment *segPtr;
  607.     int lineLength;
  608.  
  609.     if (count < 0) {
  610.     TkTextIndexBackChars(srcPtr, -count, dstPtr);
  611.     return;
  612.     }
  613.  
  614.     *dstPtr = *srcPtr;
  615.     dstPtr->charIndex += count;
  616.     while (1) {
  617.     /*
  618.      * Compute the length of the current line.
  619.      */
  620.  
  621.     lineLength = 0;
  622.     for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  623.         segPtr = segPtr->nextPtr) {
  624.         lineLength += segPtr->size;
  625.     }
  626.  
  627.     /*
  628.      * If the new index is in the same line then we're done.
  629.      * Otherwise go on to the next line.
  630.      */
  631.  
  632.     if (dstPtr->charIndex < lineLength) {
  633.         return;
  634.     }
  635.     dstPtr->charIndex -= lineLength;
  636.     linePtr = TkBTreeNextLine(dstPtr->linePtr);
  637.     if (linePtr == NULL) {
  638.         dstPtr->charIndex = lineLength - 1;
  639.         return;
  640.     }
  641.     dstPtr->linePtr = linePtr;
  642.     }
  643. }
  644.  
  645. /*
  646.  *----------------------------------------------------------------------
  647.  *
  648.  * TkTextIndexBackChars --
  649.  *
  650.  *    Given an index for a text widget, this procedure creates a
  651.  *    new index that points "count" characters earlier than the
  652.  *    source index.
  653.  *
  654.  * Results:
  655.  *    *dstPtr is modified to refer to the character "count" characters
  656.  *    before srcPtr, or to the first character in the file if there aren't
  657.  *    "count" characters earlier than srcPtr.
  658.  *
  659.  * Side effects:
  660.  *    None.
  661.  *
  662.  *----------------------------------------------------------------------
  663.  */
  664.  
  665. void
  666. TkTextIndexBackChars(srcPtr, count, dstPtr)
  667.     TkTextIndex *srcPtr;        /* Source index. */
  668.     int count;                /* How many characters backward to
  669.                      * move.  May be negative. */
  670.     TkTextIndex *dstPtr;        /* Destination index: gets modified. */
  671. {
  672.     TkTextSegment *segPtr;
  673.     int lineIndex;
  674.  
  675.     if (count < 0) {
  676.     TkTextIndexForwChars(srcPtr, -count, dstPtr);
  677.     return;
  678.     }
  679.  
  680.     *dstPtr = *srcPtr;
  681.     dstPtr->charIndex -= count;
  682.     lineIndex = -1;
  683.     while (dstPtr->charIndex < 0) {
  684.     /*
  685.      * Move back one line in the text.  If we run off the beginning
  686.      * of the file then just return the first character in the text.
  687.      */
  688.  
  689.     if (lineIndex < 0) {
  690.         lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
  691.     }
  692.     if (lineIndex == 0) {
  693.         dstPtr->charIndex = 0;
  694.         return;
  695.     }
  696.     lineIndex--;
  697.     dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
  698.  
  699.     /*
  700.      * Compute the length of the line and add that to dstPtr->charIndex.
  701.      */
  702.  
  703.     for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  704.         segPtr = segPtr->nextPtr) {
  705.         dstPtr->charIndex += segPtr->size;
  706.     }
  707.     }
  708. }
  709.  
  710. /*
  711.  *----------------------------------------------------------------------
  712.  *
  713.  * StartEnd --
  714.  *
  715.  *    This procedure handles modifiers like "wordstart" and "lineend"
  716.  *    to adjust indices forwards or backwards.
  717.  *
  718.  * Results:
  719.  *    If the modifier is successfully parsed then the return value
  720.  *    is the address of the first character after the modifier, and
  721.  *    *indexPtr is updated to reflect the modifier. If there is a
  722.  *    syntax error in the modifier then NULL is returned.
  723.  *
  724.  * Side effects:
  725.  *    None.
  726.  *
  727.  *----------------------------------------------------------------------
  728.  */
  729.  
  730. static char *
  731. StartEnd(string, indexPtr)
  732.     char *string;        /* String to parse for additional info
  733.                  * about modifier (count and units). 
  734.                  * Points to first character of modifer
  735.                  * word. */
  736.     TkTextIndex *indexPtr;    /* Index to mdoify based on string. */
  737. {
  738.     char *p;
  739.     int c, offset;
  740.     size_t length;
  741.     register TkTextSegment *segPtr;
  742.  
  743.     /*
  744.      * Find the end of the modifier word.
  745.      */
  746.  
  747.     for (p = string; isalnum(UCHAR(*p)); p++) {
  748.     /* Empty loop body. */
  749.     }
  750.     length = p-string;
  751.     if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
  752.         && (length >= 5)) {
  753.     indexPtr->charIndex = 0;
  754.     for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
  755.         segPtr = segPtr->nextPtr) {
  756.         indexPtr->charIndex += segPtr->size;
  757.     }
  758.     indexPtr->charIndex -= 1;
  759.     } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
  760.         && (length >= 5)) {
  761.     indexPtr->charIndex = 0;
  762.     } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
  763.         && (length >= 5)) {
  764.     int firstChar = 1;
  765.  
  766.     /*
  767.      * If the current character isn't part of a word then just move
  768.      * forward one character.  Otherwise move forward until finding
  769.      * a character that isn't part of a word and stop there.
  770.      */
  771.  
  772.     segPtr = TkTextIndexToSeg(indexPtr, &offset);
  773.     while (1) {
  774.         if (segPtr->typePtr == &tkTextCharType) {
  775.         c = segPtr->body.chars[offset];
  776.         if (!isalnum(UCHAR(c)) && (c != '_')) {
  777.             break;
  778.         }
  779.         firstChar = 0;
  780.         }
  781.         offset += 1;
  782.         indexPtr->charIndex += 1;
  783.         if (offset >= segPtr->size) {
  784.         segPtr = TkTextIndexToSeg(indexPtr, &offset);
  785.         }
  786.     }
  787.     if (firstChar) {
  788.         TkTextIndexForwChars(indexPtr, 1, indexPtr);
  789.     }
  790.     } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
  791.         && (length >= 5)) {
  792.     int firstChar = 1;
  793.  
  794.     /*
  795.      * Starting with the current character, look for one that's not
  796.      * part of a word and keep moving backward until you find one.
  797.      * Then if the character found wasn't the first one, move forward
  798.      * again one position.
  799.      */
  800.  
  801.     segPtr = TkTextIndexToSeg(indexPtr, &offset);
  802.     while (1) {
  803.         if (segPtr->typePtr == &tkTextCharType) {
  804.         c = segPtr->body.chars[offset];
  805.         if (!isalnum(UCHAR(c)) && (c != '_')) {
  806.             break;
  807.         }
  808.         firstChar = 0;
  809.         }
  810.         offset -= 1;
  811.         indexPtr->charIndex -= 1;
  812.         if (offset < 0) {
  813.         if (indexPtr->charIndex < 0) {
  814.             indexPtr->charIndex = 0;
  815.             goto done;
  816.         }
  817.         segPtr = TkTextIndexToSeg(indexPtr, &offset);
  818.         }
  819.     }
  820.     if (!firstChar) {
  821.         TkTextIndexForwChars(indexPtr, 1, indexPtr);
  822.     }
  823.     } else {
  824.     return NULL;
  825.     }
  826.     done:
  827.     return p;
  828. }
  829.